
//redis热点数据动态缓存策略

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <thread>
#include <chrono>
#include <hiredis/hiredis.h>

// 缓存配置
const int CACHE_CAPACITY = 10000;       // 缓存最大容量
const double DECAY_RATE = 0.8;          // 热度衰减率
const int DECAY_INTERVAL_SECONDS = 3600; // 衰减间隔(秒)
const std::string REDIS_HOST = "localhost";
const int REDIS_PORT = 6379;

// Redis连接封装类
class RedisClient {
private:
    redisContext* ctx;

public:
    RedisClient(const std::string& host, int port) {
        ctx = redisConnect(host.c_str(), port);
        if (ctx == nullptr || ctx->err) {
            if (ctx) {
                std::cerr << "Redis连接错误: " << ctx->errstr << std::endl;
                redisFree(ctx);
            } else {
                std::cerr << "Redis连接错误: 无法分配内存" << std::endl;
            }
            exit(1);
        }
    }

    ~RedisClient() {
        if (ctx) {
            redisFree(ctx);
        }
    }

    redisReply* command(const char* format, ...) {
        va_list args;
        va_start(args, format);
        redisReply* reply = (redisReply*)redisvCommand(ctx, format, args);
        va_end(args);
        return reply;
    }

    bool exists(const std::string& key) {
        redisReply* reply = command("EXISTS %s", key.c_str());
        bool result = (reply->type == REDIS_REPLY_INTEGER && reply->integer == 1);
        freeReplyObject(reply);
        return result;
    }

    void hset(const std::string& key, const std::unordered_map<std::string, std::string>& fields) {
        std::vector<const char*> args;
        args.push_back(key.c_str());
        
        for (const auto& pair : fields) {
            args.push_back(pair.first.c_str());
            args.push_back(pair.second.c_str());
        }
        
        redisReply* reply = (redisReply*)redisCommandArgv(
            ctx, args.size(), args.data(), nullptr
        );
        freeReplyObject(reply);
    }

    std::unordered_map<std::string, std::string> hgetall(const std::string& key) {
        std::unordered_map<std::string, std::string> result;
        redisReply* reply = command("HGETALL %s", key.c_str());
        
        if (reply->type == REDIS_REPLY_ARRAY) {
            for (size_t i = 0; i < reply->elements; i += 2) {
                std::string field = reply->element[i]->str;
                std::string value = reply->element[i+1]->str;
                result[field] = value;
            }
        }
        
        freeReplyObject(reply);
        return result;
    }

    void zincrby(const std::string& key, double increment, const std::string& member) {
        redisReply* reply = command("ZINCRBY %s %f %s", key.c_str(), increment, member.c_str());
        freeReplyObject(reply);
    }

    void zadd(const std::string& key, double score, const std::string& member) {
        redisReply* reply = command("ZADD %s %f %s", key.c_str(), score, member.c_str());
        freeReplyObject(reply);
    }

    long long zcard(const std::string& key) {
        redisReply* reply = command("ZCARD %s", key.c_str());
        long long result = reply->integer;
        freeReplyObject(reply);
        return result;
    }

    std::vector<std::string> zrange(const std::string& key, long long start, long long end) {
        std::vector<std::string> result;
        redisReply* reply = command("ZRANGE %s %lld %lld", key.c_str(), start, end);
        
        if (reply->type == REDIS_REPLY_ARRAY) {
            for (size_t i = 0; i < reply->elements; ++i) {
                result.push_back(reply->element[i]->str);
            }
        }
        
        freeReplyObject(reply);
        return result;
    }

    void zremrangebyrank(const std::string& key, long long start, long long end) {
        redisReply* reply = command("ZREMRANGEBYRANK %s %lld %lld", key.c_str(), start, end);
        freeReplyObject(reply);
    }

    void del(const std::string& key) {
        redisReply* reply = command("DEL %s", key.c_str());
        freeReplyObject(reply);
    }

    // 执行Lua脚本衰减热度
    void decayHotScores(const std::string& zsetKey) {
        const char* luaScript = R"(
            local hotKeys = redis.call('ZRANGE', KEYS[1], 0, -1)
            for _, key in ipairs(hotKeys) do
                local score = redis.call('ZSCORE', KEYS[1], key)
                if score then
                    redis.call('ZADD', KEYS[1], math.floor(score * tonumber(ARGV[1])), key)
                end
            end
            return 1
        )";
        
        redisReply* reply = command(
            "EVAL %s 1 %s %f", 
            luaScript, 
            zsetKey.c_str(), 
            DECAY_RATE
        );
        freeReplyObject(reply);
    }
};

// 模拟数据库查询
std::unordered_map<std::string, std::string> dbQuery(const std::string& dataId) {
    // 实际应用中这里会查询数据库
    return {
        {"id", dataId},
        {"name", "商品" + dataId},
        {"price", std::to_string(100 + std::stoi(dataId) % 900)},
        {"stock", std::to_string(1000 + std::stoi(dataId) % 9000)}
    };
}

// 热点缓存管理器
class HotDataCache {
private:
    RedisClient redis;
    std::thread decayThread;
    bool running;

    // 热度衰减线程函数
    void decayLoop() {
        while (running) {
            std::this_thread::sleep_for(std::chrono::seconds(DECAY_INTERVAL_SECONDS));
            std::cout << "执行热度衰减..." << std::endl;
            redis.decayHotScores("cache:hot");
        }
    }

    // 检查并淘汰超出容量的缓存
    void evictIfNeeded() {
        long long currentSize = redis.zcard("cache:hot");
        if (currentSize > CACHE_CAPACITY) {
            long long excess = currentSize - CACHE_CAPACITY;
            std::cout << "缓存容量超出，淘汰 " << excess << " 条低热度数据" << std::endl;
            
            // 获取要淘汰的数据ID
            std::vector<std::string> toEvict = redis.zrange("cache:hot", 0, excess - 1);
            
            // 从ZSet中删除
            redis.zremrangebyrank("cache:hot", 0, excess - 1);
            
            // 从缓存中删除实际数据
            for (const std::string& id : toEvict) {
                redis.del("cache:data:" + id);
            }
        }
    }

public:
    HotDataCache() : redis(REDIS_HOST, REDIS_PORT), running(true) {
        // 启动热度衰减线程
        decayThread = std::thread(&HotDataCache::decayLoop, this);
    }

    ~HotDataCache() {
        running = false;
        if (decayThread.joinable()) {
            decayThread.join();
        }
    }

    // 获取数据
    std::unordered_map<std::string, std::string> getData(const std::string& dataId) {
        std::string cacheKey = "cache:data:" + dataId;
        
        // 1. 检查缓存
        if (redis.exists(cacheKey)) {
            // 缓存命中，更新热度
            redis.zincrby("cache:hot", 1.0, dataId);
            return redis.hgetall(cacheKey);
        }
        
        // 2. 缓存未命中，从数据库加载
        std::cout << "缓存未命中，从数据库加载数据: " << dataId << std::endl;
        auto data = dbQuery(dataId);
        if (data.empty()) {
            return {};
        }
        
        // 3. 存入缓存
        redis.hset(cacheKey, data);
        redis.zadd("cache:hot", 1.0, dataId);  // 初始热度为1
        
        // 4. 检查并淘汰超出容量的数据
        evictIfNeeded();
        
        return data;
    }

    // 获取热点数据排名
    std::vector<std::pair<std::string, double>> getTopHotData(int count) {
        std::vector<std::pair<std::string, double>> result;
        
        // 执行ZRANGE命令获取带分数的结果
        redisReply* reply = redis.command(
            "ZREVRANGE %s %d %d WITHSCORES", 
            "cache:hot", 
            0, 
            count - 1
        );
        
        if (reply->type == REDIS_REPLY_ARRAY) {
            for (size_t i = 0; i < reply->elements; i += 2) {
                std::string id = reply->element[i]->str;
                double score = std::stod(reply->element[i+1]->str);
                result.emplace_back(id, score);
            }
        }
        
        freeReplyObject(reply);
        return result;
    }
};

// 测试函数
int main() {
    try {
        HotDataCache cache;
        
        // 模拟一系列数据访问
        for (int i = 0; i < 500; ++i) {
            // 让ID为10-20的数据成为热点
            int id = (i % 100) < 10 ? (10 + i % 10) : (100 + i % 900);
            cache.getData(std::to_string(id));
            
            // 每100次访问打印一次热点排名
            if (i % 100 == 0 && i > 0) {
                std::cout << "\nTop 5 热点数据:" << std::endl;
                auto topData = cache.getTopHotData(5);
                for (const auto& item : topData) {
                    std::cout << "ID: " << item.first << ", 热度: " << item.second << std::endl;
                }
                std::cout << std::endl;
            }
        }
        
        std::cout << "测试完成" << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "发生错误: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}
